package org.sfnelson.sk.server;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.sfnelson.sk.client.data.Loot;
import org.sfnelson.sk.client.data.User;
import org.sfnelson.sk.client.rpc.Event;
import org.sfnelson.sk.client.rpc.EventService;
import org.sfnelson.sk.client.rpc.Event.Type;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

@SuppressWarnings("serial")
public class EventServiceImpl extends RemoteServiceServlet implements EventService {

	private final Random random = new Random();

	public synchronized List<org.sfnelson.sk.client.rpc.Event> created(Date last, String name) {

		User user = getUser(name);

		if (user != null) {
			System.err.println("attempt to add duplicate user: " + name);

			return listen(last);
		} else {
			user = new User(name, random.nextLong());

			store(user);
			store(Event.created(user));

			return joined(last, user);
		}
	}

	public synchronized List<org.sfnelson.sk.client.rpc.Event> joined(Date last, User user) {

		User u = getUser(user.getId());

		if (u == null) {
			System.err.println("attempt to join non-existent user: " + user.getName());
			return null;
		}

		//System.out.println(getLastEvent(u, Type.JOINED));
		//System.out.println(getLastEvent(u, Type.LEFT));

		/*
		if (raid.contains(user)) {
			System.err.println("attempt to join a user who is already joined: " + user.getName());
			return null;
		}

		raid.add(user);
		 */

		store(Event.joined(user));

		return listen(last);
	}

	public synchronized List<org.sfnelson.sk.client.rpc.Event> left(Date last, User user) {
		/*if (!users.containsKey(user)) {
			System.err.println("attempt to remove a non-existent user: " + user.name);
			return null;
		}*/

		/*
		if (!raid.contains(user)) {
			System.err.println("attempt to remove a user who has not joined: " + user.name);
			return null;
		}
		 */

		//raid.remove(user);

		store(Event.left(user));

		return listen(last);
	}

	public synchronized List<org.sfnelson.sk.client.rpc.Event> receivedLoot(Date last, User user, Loot loot, String reason) {
		/*if (!users.containsKey(user)) {
			System.err.println("attempt to assign loot to a non-existent user: " + user.name);
			return null;
		}*/

		/*
		if (!raid.contains(user)) {
			System.err.println("attempt to assign loot to a user who is not in the raid: " + user.name);
			return null;
		}
		 */

		store(Event.receivedLoot(user, loot, reason));

		return listen(last);
	}

	public List<org.sfnelson.sk.client.rpc.Event> listen(Date lastEvent) {
		return getEvents(lastEvent);
	}

	private void store(User user) {
		PersistenceManager pm = PMF.getPersistenceManager();

		try {
			pm.makePersistent(user);
		} finally {
			pm.close();
		}
	}

	private void store(Event event) {
		PersistenceManager pm = PMF.getPersistenceManager();

		try {
			pm.makePersistent(event);
		} finally {
			pm.close();
		}
	}

	@SuppressWarnings("unchecked")
	private List<Event> getEvents(Date last) {
		PersistenceManager pm = PMF.getPersistenceManager();

		List<Event> events = new ArrayList<Event>();
		try {
			List<Event> result;
			if (last != null) {
				Query q = pm.newQuery(Event.class, "date != null && date > last");
				q.declareParameters("java.util.Date last");
				q.setOrdering("date");
				result = (List<Event>) q.execute(last);
			} else {
				Query q = pm.newQuery(Event.class, "date != null");
				q.setOrdering("date");
				result = (List<Event>) q.execute(last);
			}
			for (Event e: result) {
				events.add(e);
				// force loading of child objects
				e.asEvent();
			}
		} finally {
			pm.close();
		}

		for (Event e: events) {
			Long userId = e.getUserId();
			User user = getUser(userId);
			if (user != null) {
				e.setUser(user);
			}
		}

		return events;
	}

	@SuppressWarnings("unchecked")
	private Event getLastEvent(User user, Type type) {
		PersistenceManager pm = PMF.getPersistenceManager();

		Event last = null;
		try {
			Query q = pm.newQuery(Event.class, "userId == uid && type == ntype");
			q.declareParameters("java.lang.Long uid, java.lang.String ntype");
			q.setOrdering("date");
			List<Event> events = (List<Event>) q.execute(user.getId(), type);
			if (!events.isEmpty()) {
				last = events.get(0);
			}
		} finally {
			pm.close();
		}
		return last;
	}

	@SuppressWarnings("unchecked")
	public User getUser(String name) {
		PersistenceManager pm = PMF.getPersistenceManager();

		User user = null;
		try {
			Query q = pm.newQuery(User.class, "name == nname");
			q.declareParameters("java.lang.String nname");
			List<User> users = (List<User>)q.execute(name);
			if (!users.isEmpty()) {
				user = users.get(0);
			}
		} finally {
			pm.close();
		}

		return user;
	}

	@SuppressWarnings("unchecked")
	public User getUser(Long userId) {
		PersistenceManager pm = PMF.getPersistenceManager();

		User user = null;
		try {
			Query q = pm.newQuery(User.class, "id == nid");
			q.declareParameters("java.lang.Long nid");
			List<User> users = (List<User>)q.execute(userId);
			if (!users.isEmpty()) {
				user = users.get(0);
			}
		} finally {
			pm.close();
		}

		return user;
	}
}
